#define EXCEPTION_LR    0xFFFFFFF9
               AREA |.text|, CODE, READONLY
/**
 * thread cut-in implementation
 * @prototype: void mx_thread_switch (mx_void_pt from, mx_void_pt to);
 * F_I_X_M_E: not implement the floating-point mode
 * NOTE: actually we can implement it by PENDSV, here is just for fun
 */
; mx_thread_switch\
;                PROC
;                EXPORT mx_thread_switch
;                /* save context of `from thread`, r0 and r1 need not to save */
;                // self-definition region, save to stack bottom
;                ; the       `#-(4*1)` is EXC_RETURN, it's used only in interrupt
;                str r4, [r0, #-(4*2)]   ; r4
;                str r5, [r0, #-(4*3)]   ; r5
;                str r6, [r0, #-(4*4)]   ; r6
;                str r7, [r0, #-(4*5)]   ; r7
;                str r8, [r0, #-(4*6)]   ; r8
;                str r9, [r0, #-(4*7)]   ; r9
;                str r10, [r0, #-(4*8)]  ; r10
;                str r11, [r0, #-(4*9)]  ; r11
;                sub r4, sp, #32         ; reserved space for r0~r3/r12/lr/pc/xpsr (4*8=32)
;                str r4, [r0, #-(4*10)]  ; sp
;                mrs r4, PRIMASK
;                str r4, [r0, #-(4*11)]  ; PRIMASK
;                mrs r4, FAULTMASK
;                str r4, [r0, #-(4*12)]  ; FAULTMASK
;                mrs r4, BASEPRI
;                str r4, [r0, #-(4*13)]  ; BASEPRI
;                mrs r4, CONTROL
;                str r4, [r0, #-(4*14)]  ; CONTROL
;                // hardware-auto-save region, save to stack top
;                mrs r4, psr
;                ldr r5, =EXCEPTION_LR
;                push {r4}               ; ISSUE: push {r4, lr, r5, r12, r3, r2, r1, r0} is wrong sequence, i don't know why
;                push {lr}
;                push {r5}
;                push {r12}
;                push {r3}
;                push {r2}
;                push {r1}
;                push {r0}
; 
;                /* restore context of target thread */
;                // self-definition region, from stack bottom
;                ; the       `#-(4*1)` is EXC_RETURN, it's used only in interrupt
;                ldr r4, [r1, #-(4*2)]   ; r4
;                ldr r5, [r1, #-(4*3)]   ; r5
;                ldr r6, [r1, #-(4*4)]   ; r6
;                ldr r7, [r1, #-(4*5)]   ; r7
;                ldr r8, [r1, #-(4*6)]   ; r8
;                ldr r9, [r1, #-(4*7)]   ; r9
;                ldr r10, [r1, #-(4*8)]  ; r10
;                ldr r11, [r1, #-(4*9)]  ; r11
;                ldr sp, [r1, #-(4*10)]  ; sp
;                ldr r3, [r1, #-(4*11)]  ; PRIMASK
;                msr PRIMASK, r3
;                ldr r3, [r1, #-(4*12)]  ; FAULTMASK
;                msr FAULTMASK, r3
;                ldr r3, [r1, #-(4*13)]  ; BASEPRI
;                msr BASEPRI, r3
;                ldr r3, [r1, #-(4*14)]  ; CONTROL
;                msr CONTROL, r3
;                // hardware-auto-save region, from stack top
;                ; set the bit[0] of PC on the stack which pushed by hardware to 1
;                ldr r3, [sp, #(4*6)]    ; pc
;                orr r3, r3, #1
;                str r3, [sp, #(4*6)]
;                ; pop registers
;                ldr r3, [sp, #(4*7)]    ; xpsr
;                msr psr, r3
;                pop {r0}
;                pop {r1}
;                pop {r2}
;                pop {r3}
;                pop {r12}
;                pop {lr}                ; in practice lr need not pop
;                add sp, sp, #8          ; move sp to exclude hardware-auto-save region
;                ldr pc, [sp, #-8]
;                ENDP

/**
 * thread switch in interrupt environment
 * @prototype: void mx_thread_switch_interrupt (mx_void_pt fromcontext, mx_void_pt from, mx_void_pt to);
 * NOTE: r0 = fromcontext(stack top of `from thread`), r1 = stack bottom of `from thread`, r2 = stack bottom of `to thread`
 * FIXME: not implement the floating-point mode
 */
mx_thread_switch_interrupt\
               PROC
               EXPORT mx_thread_switch_interrupt
               /* save context of `from thread`. copy the `save self-definition region` from stack top to stack bottom
                  now the stack looks {top: CONTROL BASEPRI FAULTMASK PRIMASK sp(top) r11~r4 lr(EXC_RETURN) } */
               ldr r3, [r0, #(4*13)]   ; EXC_RETURN
               str r3, [r1, #-(4*1)]
               ldr r3, [r0, #(4*12)]   ; r4
               str r3, [r1, #-(4*2)]
               ldr r3, [r0, #(4*11)]   ; r5
               str r3, [r1, #-(4*3)]
               ldr r3, [r0, #(4*10)]   ; r6
               str r3, [r1, #-(4*4)]
               ldr r3, [r0, #(4*9)]    ; r7
               str r3, [r1, #-(4*5)]
               ldr r3, [r0, #(4*8)]    ; r8
               str r3, [r1, #-(4*6)]
               ldr r3, [r0, #(4*7)]    ; r9
               str r3, [r1, #-(4*7)]
               ldr r3, [r0, #(4*6)]    ; r10
               str r3, [r1, #-(4*8)]
               ldr r3, [r0, #(4*5)]    ; r11
               str r3, [r1, #-(4*9)]
               ldr r3, [r0, #(4*4)]    ; sp
               str r3, [r1, #-(4*10)]
               ldr r3, [r0, #(4*3)]    ; PRIMASK
               str r3, [r1, #-(4*11)]
               ldr r3, [r0, #(4*2)]    ; FAULTMASK
               str r3, [r1, #-(4*12)]
               ldr r3, [r0, #(4*1)]    ; BASEPRI
               str r3, [r1, #-(4*13)]
               ldr r3, [r0, #(4*0)]    ; CONTROL
               str r3, [r1, #-(4*14)]
               /* restore context of target thread */
               // self-definition region, from stack bottom
               ldr r4, [r2, #-(4*2)]   ; r4
               ldr r5, [r2, #-(4*3)]   ; r5
               ldr r6, [r2, #-(4*4)]   ; r6
               ldr r7, [r2, #-(4*5)]   ; r7
               ldr r8, [r2, #-(4*6)]   ; r8
               ldr r9, [r2, #-(4*7)]   ; r9
               ldr r10, [r2, #-(4*8)]  ; r10
               ldr r11, [r2, #-(4*9)]  ; r11
               ldr sp, [r2, #-(4*10)]  ; sp
               ldr r3, [r2, #-(4*11)]  ; PRIMASK
               msr PRIMASK, r3
               ldr r3, [r2, #-(4*12)]  ; FAULTMASK
               msr FAULTMASK, r3
               ldr r3, [r2, #-(4*13)]  ; BASEPRI
               msr BASEPRI, r3
               ldr r3, [r2, #-(4*14)]  ; CONTROL
               msr CONTROL, r3
               // hardware-auto-save region, needn't to do anything
               ; set the bit[0] of PC on the stack which pushed by ourself to 0. ISSUE: If i remove this step, it can still work sometimes with PC[0] = 1, i don't know the resion why ?
               ldr r3, [sp, #(4*6)]    ; pc
               bic r1, r3, #0xfffffffe
               cbz r1, intr_exit
               bic r3, #1
               str r3 , [sp, #(4*6)]
               ; set the PSR.T to 1, otherwise, an exception will triggered (SCB->HFSR = 0x40000000, SCB->CFSR = 0x20000)
               ldr r3, [sp, #(4*7)]    ; psr
               orr r3, #0x01000000
               str r3, [sp, #(4*7)]
               ; do exception exit sequence, move EXC_RETURN to PC
intr_exit      ldr pc, [r2, #-(4*1)]   ; EXC_RETURN
               ENDP

               END
